/*
 * ReflectionUtils.java - Copyright Mar 8, 2012 Z Networks Group, Inc. All rights reserved.
 * Z Networks Group PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.andromeda.skadi.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 * @author Dickinson Roman Arismendy Torres
 */
public class ReflectionUtils {

    /**
     * Busca el metodo con el nombre dado en la clase dada y que tenga cero o un
     * parametro de la clase da argumento dada. Si hay dos metodos con el mismo
     * nombre se preferirá el que tiene parametros.
     */
    public static Method searchMethod(String methodName, Class targetClass, Class argClass) {
        Method[] classMethods = targetClass.getDeclaredMethods();
        Method[] publicMethods = targetClass.getMethods();
        List<Method> methods = new ArrayList<Method>();
        methods.addAll(Arrays.asList(classMethods));
        methods.addAll(Arrays.asList(publicMethods));
        Method noParamsMethod = null;
        Method paramMethod = null;
        for (Method _method : methods) {
            if (_method.getName().equals(methodName)) {
                Class<?>[] parameterTypes = _method.getParameterTypes();
                if (parameterTypes.length == 0) {
                    noParamsMethod = _method;
                }
                if (parameterTypes.length == 1 && argClass != null && argClass.equals(parameterTypes[0])) {
                    paramMethod = _method;
                }
            }
        }
        if (paramMethod == null && noParamsMethod == null) {
            throw new RuntimeException(new NoSuchMethodException(methodName + " method not found."));
        }
        Method selected = paramMethod == null ? noParamsMethod : paramMethod;
        selected.setAccessible(true);
        return selected;
    }

    /**
     * Permite establecer el valor de un atributo de un objeto así el
     * modificador de acceso del atributo no lo permita.
     *
     * @param target El objeto al cual se le establecerá el valor del atributo.
     * @param attributeName el nombre del atributo.
     * @param value el valor del atributo.
     * @return
     * <code>true</code> si fue posible establecer el valor del atributo,
     * <code>false</code> si no se encontró ningún atributo con el nombre dado.
     */
    public static boolean setAttribute(Object target, String attributeName, Object value) {
        Field[] declaredFields = target.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getName().equals(attributeName)) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                    try {
                        field.set(target, value);
                        return true;
                    } catch (Exception ex) {
                        throw new ReflectionException("Error while setting value to " + attributeName
                                + " in class " + target.getClass().getName() + ".", ex);
                    }
                }
            }
        }
        return false;
    }
}
